package org.xxpay.pay.channel.sqpay;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.entity.PayOrder;
import org.xxpay.pay.channel.BasePayNotify;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: zhuxiao
 * @date: 18/08/09
 * @description: 双乾支付通知
 */
@Service
public class SqpayPayNotifyService extends BasePayNotify {

    private static final MyLog _log = MyLog.getLog(SqpayPayNotifyService.class);

    @Override
    public String getChannelName() {
        return SqpayConfig.CHANNEL_NAME_SQPAY;
    }

    @Override
    public JSONObject doNotify(Object notifyData) {
        String logPrefix = "【处理双乾支付回调】";
        _log.info("====== 开始处理双乾支付回调通知 ======");
        HttpServletRequest request = (HttpServletRequest) notifyData;
        JSONObject retObj = buildRetObj();
        Map<String, Object> payContext = new HashMap();
        PayOrder payOrder;

        String MerNo = request.getParameter("MerNo");
        String BillNo = request.getParameter("BillNo");
        String Amount = request.getParameter("Amount");
        String Succeed = request.getParameter("Succeed");
        String Result  = request.getParameter("Result");
        String MD5info = request.getParameter("MD5info");
        String MerRemark  = request.getParameter("MerRemark");

        Map<String,String> params = new HashMap<>();
        params.put("MerNo", MerNo);
        params.put("BillNo", BillNo);
        params.put("Amount", Amount);
        params.put("Succeed", Succeed);
        params.put("Result", Result);
        params.put("MD5info", MD5info);
        params.put("MerRemark", MerRemark);
        _log.info("{}通知参数:{}", logPrefix, params);

        String respString = "处理失败";
        try {
            payContext.put("parameters", params);
            if(!verifyPayParams(payContext)) {
                retObj.put(PayConstant.RESPONSE_RESULT, "验证数据没有通过");
                return retObj;
            }
            payOrder = (PayOrder) payContext.get("payOrder");
            // 处理订单
            byte payStatus = payOrder.getStatus(); // 0：订单生成，1：支付中，-1：支付失败，2：支付成功，3：业务处理完成，-2：订单过期
            if (payStatus != PayConstant.PAY_STATUS_SUCCESS && payStatus != PayConstant.PAY_STATUS_COMPLETE) {
                int updatePayOrderRows = commonService.payOrderService.updateStatus4Success(payOrder.getPayOrderId(), null);
                if (updatePayOrderRows != 1) {
                    _log.error("{}更新支付状态失败,将payOrderId={},更新payStatus={}失败", logPrefix, payOrder.getPayOrderId(), PayConstant.PAY_STATUS_SUCCESS);
                    retObj.put(PayConstant.RESPONSE_RESULT, "处理订单失败");
                    return retObj;
                }
                _log.error("{}更新支付状态成功,将payOrderId={},更新payStatus={}成功", logPrefix, payOrder.getPayOrderId(), PayConstant.PAY_STATUS_SUCCESS);
                payOrder.setStatus(PayConstant.PAY_STATUS_SUCCESS);
            }
            // 业务系统后端通知
            baseNotify4MchPay.doNotify(payOrder, true);
            _log.info("====== 完成处理双乾支付回调通知 ======");
            respString = SqpayConfig.RETURN_VALUE_SUCCESS;
        } catch (Exception e) {
            _log.error(e, logPrefix + "处理异常");
        }
        retObj.put(PayConstant.RESPONSE_RESULT, respString);
        return retObj;

    }

    /**
     * 验证支付通知参数
     * @return
     */
    public boolean verifyPayParams(Map<String, Object> payContext) {
        Map<String, String> params = (Map<String,String>)payContext.get("parameters");

        //校验结果是否成功
        String Succeed = params.get("Succeed");
        if(!"88".equals(Succeed)) {
            _log.error("Succeed={}", Succeed);
            payContext.put("retMsg", "notify data failed");
            return false;
        }

        String MerNo = params.get("MerNo");		// 商户订单号
        String BillNo = params.get("BillNo");		// 商户订单号
        String Amount = params.get("Amount"); 		// 支付金额
        if (StringUtils.isEmpty(MerNo)) {
            _log.error("Notify parameter MerNo is empty. MerNo={}", MerNo);
            payContext.put("retMsg", "MerNo is empty");
            return false;
        }
        if (StringUtils.isEmpty(BillNo)) {
            _log.error("Notify parameter BillNo is empty. BillNo={}", BillNo);
            payContext.put("retMsg", "BillNo is empty");
            return false;
        }
        if (StringUtils.isEmpty(Amount)) {
            _log.error("Notify parameter Amount is empty. Amount={}", Amount);
            payContext.put("retMsg", "Amount is empty");
            return false;
        }
        String errorMessage;
        // 查询payOrder记录
        String payOrderId = BillNo;
        PayOrder payOrder = commonService.payOrderService.findByPayOrderId(payOrderId);
        if (payOrder == null) {
            _log.error("Can't found payOrder form db. payOrderId={}, ", payOrderId);
            payContext.put("retMsg", "Can't found payOrder");
            return false;
        }

        SqpayConfig sqpayConfig = new SqpayConfig(getPayParam(payOrder));

        String MD5key = sqpayConfig.getMD5key();
        String md5str = org.xxpay.pay.channel.sqpay.util.MD5Util.signMap(new String[]{MerNo, BillNo, Amount, String.valueOf(Succeed)}, MD5key, "RES");
        // 验证签名
        if (!md5str.equals(params.get("MD5info"))) {
            errorMessage = "check sign failed.";
            _log.error("Notify parameter {}", errorMessage);
            payContext.put("retMsg", errorMessage);
            return false;
        }

        // 核对金额
        long outPayAmt = new BigDecimal(Amount).multiply(new BigDecimal(100)).longValue();
        long dbPayAmt = payOrder.getAmount().longValue();
        if (dbPayAmt != outPayAmt) {
            _log.error("db payOrder record payPrice not equals total_amount. Amount={},payOrderId={}", Amount, payOrderId);
            payContext.put("retMsg", "");
            return false;
        }
        payContext.put("payOrder", payOrder);
        return true;
    }

    //双乾支付前端同步跳转
    @Override
    public JSONObject doReturn(Object notifyData) {
        String logPrefix = "【处理双乾支付同步跳转】";
        _log.info("====== 开始处理双乾支付同步跳转 ======");
        HttpServletRequest request = (HttpServletRequest) notifyData;
        JSONObject retObj = buildRetObj();
        Map<String, Object> payContext = new HashMap();

        String MerNo = request.getParameter("MerNo");
        String BillNo = request.getParameter("BillNo");
        String Amount = request.getParameter("Amount");
        String Succeed = request.getParameter("Succeed");
        String Result  = request.getParameter("Result");
        String MD5info = request.getParameter("MD5info");
        String MerRemark  = request.getParameter("MerRemark");

        Map<String,String> params = new HashMap<>();
        params.put("MerNo", MerNo);
        params.put("BillNo", BillNo);
        params.put("Amount", Amount);
        params.put("Succeed", Succeed);
        params.put("Result", Result);
        params.put("MD5info", MD5info);
        params.put("MerRemark", MerRemark);
        _log.info("{}通知参数:{}", logPrefix, params);

        payContext.put("parameters", params);
        if(!verifyPayReturnParams(payContext)) {
            retObj.put(PayConstant.RESPONSE_RESULT, PayConstant.RETURN_ALIPAY_VALUE_FAIL);
            return retObj;
        }
        _log.info("{}验证支付通知数据及签名通过", logPrefix);

        PayOrder payOrder = (PayOrder)payContext.get("payOrder");
        String url = baseNotify4MchPay.createNotifyUrl(payOrder, "1");
        retObj.put(PayConstant.RESPONSE_RESULT, PayConstant.RETURN_ALIPAY_VALUE_SUCCESS);
        retObj.put(PayConstant.JUMP_URL, url);
        _log.info("====== 完成处理双乾同步跳转 ======");
        return retObj;
    }

    /**
     * 验证双乾支付结果参数
     * @return
     */
    public boolean verifyPayReturnParams(Map<String, Object> payContext) {
        Map<String, String> params = (Map<String,String>)payContext.get("parameters");

        //校验结果是否成功
        String Succeed = params.get("Succeed");
        if(!"88".equals(Succeed)) {
            _log.error("Succeed={}", Succeed);
            payContext.put("retMsg", "notify data failed");
            return false;
        }

        String MerNo = params.get("MerNo");		// 商户号
        String BillNo = params.get("BillNo");		// 商户订单号
        String Amount = params.get("Amount"); 		// 支付金额
        if (StringUtils.isEmpty(BillNo)) {
            _log.error("Notify parameter BillNo is empty. out_trade_no={}", BillNo);
            payContext.put("retMsg", "BillNo is empty");
            return false;
        }
        if (StringUtils.isEmpty(Amount)) {
            _log.error("Notify parameter Amount is empty. Amount={}", Amount);
            payContext.put("retMsg", "Amount is empty");
            return false;
        }
        if (StringUtils.isEmpty(MerNo)) {
            _log.error("Notify parameter MerNo is empty. MerNo={}", MerNo);
            payContext.put("retMsg", "MerNo is empty");
            return false;
        }
        String errorMessage;
        // 查询payOrder记录
        String payOrderId = BillNo;
        PayOrder payOrder = commonService.payOrderService.findByPayOrderId(payOrderId);
        if (payOrder == null) {
            _log.error("Can't found payOrder form db. payOrderId={}, ", payOrderId);
            payContext.put("retMsg", "Can't found payOrder");
            return false;
        }

        SqpayConfig sqpayConfig = new SqpayConfig(getPayParam(payOrder));

        String MD5key = sqpayConfig.getMD5key();
        String md5str = org.xxpay.pay.channel.sqpay.util.MD5Util.signMap(new String[]{MerNo, BillNo, Amount, String.valueOf(Succeed)}, MD5key, "RES");
        // 验证签名
        if (!md5str.equals(params.get("MD5info"))) {
            errorMessage = "check sign failed.";
            _log.error("Notify parameter {}", errorMessage);
            payContext.put("retMsg", errorMessage);
            return false;
        }

        // 核对金额
        long outPayAmt = new BigDecimal(Amount).multiply(new BigDecimal(100)).longValue();
        long dbPayAmt = payOrder.getAmount().longValue();
        if (dbPayAmt != outPayAmt) {
            _log.error("db payOrder record payPrice not equals total_amount. Amount={},payOrderId={}", Amount, payOrderId);
            payContext.put("retMsg", "");
            return false;
        }
        payContext.put("payOrder", payOrder);
        return true;
    }

}
